package com.tools.web.interceptor.quota;

import com.tools.common.object.Force;
import com.tools.common.object.Note;
import com.tools.common.thread.Timeunit;
import com.tools.db.cache.local.LocalCache;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;

/**
 * 用 LocalCache 本地缓存实现的配额限制拦截器
 * */
@Note("使用 LocalCache 本地缓存实现的配额限制拦截器")
public final class LocalCacheQuotaManager extends AbsQuotaManager {

    @Note("LocalCache 本地缓存实例")
    private final LocalCache cache;

    @Note("拒绝访问结果存储在 HttpServletRequest 里的 name")
    private static final String DENY = "deny";

    public LocalCacheQuotaManager(LocalCache cache, QuotaSource source) {
        super(source);
        this.cache = Objects.requireNonNull(cache);
    }

    @Override
    protected boolean protected_checkIsAllow(QuotaLimit quotaLimit, HttpServletRequest request, HttpServletResponse response) throws Exception {
        String action = quotaLimit.action();
        int allow = quotaLimit.allow();
        String uniqueID = super.protected_getUniqueID(quotaLimit, request, response);
        String key = super.protected_getKey(action, uniqueID);
        Force<Integer> forceHandler = new Force<Integer>(allow, request) {
            @Override
            public Integer force(Integer source) {
                int allow = this.getParam(0, int.class);
                HttpServletRequest request = this.getParam(1, HttpServletRequest.class);
                if(source < allow) {
                    return source;
                }
                request.setAttribute(DENY, true);
                return source;
            }
        };
        this.cache.modify(key, forceHandler);
        if(request.getAttribute(DENY) == null) {
            return true;
        }
        response.getWriter().write(super.protected_getQuotaErrorMessage(quotaLimit, uniqueID));
        return false;
    }

    @Override
    public void increaseQuota(String action, Object uniqueID, long expiration, Timeunit timeunit) {
        String key = super.protected_getKey(action, uniqueID.toString());
        long expirationMill = Timeunit.toMill(expiration, timeunit);
        Force<Integer> forceHandle = new Force<Integer>() {
            @Override
            public Integer force(Integer source) {
                return source + 1;
            }
        };
        this.cache.set(key, forceHandle, 1, expirationMill);
    }
}
